/*
 * Copyright (c) 2008-2014, RF-Embedded GmbH
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 *  1. Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, 
 *     this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.rfe.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import com.rfe.JrfeGlobal;
import com.rfe.device.IProtocolDeviceInterface;
import com.rfe.helper.JrfeHelper;
import com.rfe.protocol.JrfeConstants;
import com.rfe.protocol.JrfeProtocolException;
import com.rfe.protocol.JrfeProtocolHandler;
import com.rfe.protocol.JrfeTagEvent;
import com.rfe.protocol.JrfeConstants.eRFE_CURRENT_READER_STATE;

/**
 * The basic test program, with the RF-Embedded test routines
 * If you want to insert your own test routines just use the TestProgramExtended
 */
public class TestProgram {
	
	/** Instance of the protocol handler */
    protected JrfeProtocolHandler _ph = null;
    /** Holds if the test program was initialized */
    protected boolean _init = false;
    /** Holds the trace level for the test */
    protected int _testTraceLevel = 0;

    /**
     * Enum to hold the connection type
     */
    public enum ConnectionType
    {
        SERAIL,
        ETHERNET
    }

    /**
     * Constructs the test program
     */
    public TestProgram()
    {
        _ph = null;
        _init = false;
    }
    
    /**
     * Initializes the test programs
     * @param device	The communication device to the reader
     * @return	Success of init function
     */
    public boolean init(IProtocolDeviceInterface device)
    {
        // create trace and turn off 
        JrfeGlobal.m_tracer = new com.rfe.test.impl.ConsoleTrace();
        JrfeGlobal.m_tracer.TraceLevel = _testTraceLevel;

        _ph = createInstance(device);

        if (_ph == null)
            return false;

        try {
			_ph.setHeartBeat(false, (short)0);
		} catch (JrfeProtocolException e) {
			e.printStackTrace();
			return false;
		}

        // connect events to the local event handler
        _ph.setListener(m_listner);

        _init = true;

        return true;
    }
    
    /**
     * First queries the test function and then executes this function
     * @return		The result of the test function
     * @throws IOException
     */
    public boolean exec() throws IOException
    {
        if (!_init)
            return false;

        //System.out.clear();
        System.out.print("\n\n\n\n\n\n\n");
        System.out.flush();
        
        System.out.println("");
        printTestTable();

        System.out.print("Please choose: > ");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String idStr = br.readLine();

        System.out.println("");

        if (idStr.startsWith("q"))
        {
            System.out.println("Exit");
            return false;
        }

        int id = Integer.parseInt(idStr);

        boolean res = false;
        
        try {
			res = callTestRoutine(id);
	        if (res == false)
	        {
	            System.out.println("Not yet implemented");
	        }

		} catch (JrfeProtocolException e) {
			System.out.println("========================== ERROR ==========================");
			e.printStackTrace();
		}


        System.out.println("\n\n\n");
        System.out.println("Press enter to proceed...");
        br.readLine();

        return true;
    }
    
    /********************************************************************************************* Test Methods */

    public void test_ReaderInfo() throws JrfeProtocolException
    {

        // get reader id
        Integer readerId = _ph.getReaderID();

        // get reader type
        Integer readerType = _ph.getReaderType();

        // get hardware revision
        Integer hwRev = _ph.getHardwareRevision();

        // get software revision
        Integer swRev = _ph.getSoftwareRevision();

        // get bootloader revision
        Integer blRev = _ph.getBootloaderRevision();

        // get current system
        Integer system = _ph.getCurrentSystem();

        // get current state
        Integer state = _ph.getCurrentState();

        // get status register
        Long statusReg = _ph.getStatusRegister();
        
        // get antenna count
        Integer antennaCount = _ph.getAntennaCount();

        // print out results
        System.out.println("Reader Information:");
        System.out.println("\t -> ReaderID       = " + JrfeHelper.toHexString(readerId));
        System.out.println("\t -> ReaderType     = " + JrfeHelper.toHexString(readerType));
        System.out.println("\t -> HardwareRev    = " + JrfeHelper.toHexString(hwRev));
        System.out.println("\t -> SoftwareRev    = " + JrfeHelper.toHexString(swRev));
        System.out.println("\t -> BootloaderRev  = " + JrfeHelper.toHexString(blRev));
        System.out.println("\t -> Current System = " + JrfeConstants.eRFE_CURRENT_SYSTEM.toString(system));
        System.out.println("\t -> Current State  = " + JrfeConstants.eRFE_CURRENT_READER_STATE.toString(state));
        System.out.println("\t -> StatusRegister = " + JrfeHelper.toHexString(statusReg));
        System.out.println("\t -> Antenna Count  = " + antennaCount);
    }

    public void test_Attenuation() throws JrfeProtocolException
    {
        System.out.println("Testing attenuation settings:");

        Short attMax;
        Short attCur;
        Short attTemp;

        // get attenuation values
        System.out.println("\t -> 1, Reading attenuation settings:");
        
        attMax = _ph.getMaxAttenuation();
        attCur = _ph.getCurrentAttenuation();
       
        System.out.println("\t\t Attenuation Max=" + attMax + " Current=" + attCur + "");
        System.out.println("");

        
        // set attenuation to fix value 10
        System.out.println("\t -> 2, Setting attenuation settings:");
        
        _ph.setAttenuation((short) 10);
        
        System.out.println("\t\t Set Attenuation to 10");
        System.out.println("");

        
        // get attenuation settings and check if the fix value is set
        System.out.println("\t -> 3, Reading attenuation settings:");
        
        attMax = _ph.getMaxAttenuation();
        attTemp = _ph.getCurrentAttenuation();
       
        System.out.println("\t\t Attenuation Max=" + attMax + " Current=" + attCur + "");
        System.out.println("\t\t Current attenuation " + attTemp + " == 10?");
        if (attTemp != 10)
        {
            System.out.println("ERROR------------------ Set Attenuation is not the Current Attenuation");
            return;
        }
        System.out.println("\t\t OK\n");

        
        // retore attenuation to the previous value
        System.out.println("\t -> 4, Restore attenuation settings:");
        
        _ph.setAttenuation(attCur);
       
        System.out.println("\t\t Set Attenuation to 0");
        System.out.println("");

        
        // check the set values again
        System.out.println("\t -> 5, Reading attenuation settings:");
       
        attMax = _ph.getMaxAttenuation();
        attCur = _ph.getCurrentAttenuation();
        
        System.out.println("\t\t Attenuation Max=" + attMax + " Current=" + attCur + "");
        System.out.println("");
    }

    public void test_Frequency() throws JrfeProtocolException
    {
        System.out.println("Testing frequency settings:");

        byte mode, maxCount;
        List<Integer> frequ;
        List<Integer> tempFrequ;

        
        // get current frequency table
        System.out.println("\t -> 1, Reading frequency settings:");
       
        mode   		= _ph.getFrequencyMode();
        maxCount	= _ph.getMaxFrequencyCount();
        frequ		= _ph.getFrequencyList();
        
        System.out.println("\t\t FrequencyTable Mode=" + mode + " Max=" + maxCount + " Current=" + frequ.size());
        for (int i = 0; i < frequ.size(); i++)
        {
            System.out.println("\t\t\t " + i + " = " + frequ.get(i));
        }
        System.out.println("");
    }

    public void test_Sensitivity() throws JrfeProtocolException
    {
        System.out.println("Testing sensitivity settings:");

        Short maxSens;
        Short minSens;
        Short curSens;
        Short temSens;
        Short actSens;

        // get current sensitivity
        System.out.println("\t -> 1, Reading sensitivity settings:");
        
        maxSens = _ph.getMaxSensitivity();
        minSens = _ph.getMinSensitivity();
        curSens = _ph.getSensitivity();
        
        System.out.println("\t\t Sensitivity Max=" + maxSens + " Min=" + minSens + " Current=" + curSens);
        System.out.println("");
    }

    public void test_Heartbeat() throws JrfeProtocolException
    {
        System.out.println("Testing Heartbeat:");

        // turn on heartbeat with an interval of 250ms
        System.out.println("\t -> 1, Setting Heartbeat ON with interval 250ms (Press Enter to Stop)");
        _lastHeartbeat = System.currentTimeMillis();
       
        _ph.setHeartBeat(true, (short) 250);

        // wait for <enter>
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
			br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}

        // turn off heartbeat
        System.out.println("\t -> 2, Setting Heartbeat OFF");
       
        _ph.setHeartBeat(false, (short) 250);
    }

    public void test_Inventory() throws JrfeProtocolException
    {
        System.out.println("Testing Cyclic Inventory:");

        System.out.println("\t -> 1, Starting Cyclic Inventory (Press Enter to Stop)");
        _tagReadCount = 0;
        long startTime = System.currentTimeMillis();
       
        // turn on cyclic inventory
        _ph.setCyclicInventory(true);

        // wait for <enter>
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
			br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}

        // turn off cyclic inventory and calculate read rate
        _ph.setCyclicInventory(false);
        
        double secs = (System.currentTimeMillis() - startTime) / 1000.0;
        System.out.println("\t -> 2, Stopped Cyclic Inventry with a ReadRate of " + (_tagReadCount / secs) + " reads/sec");
    }

    public void test_SingleInventory() throws JrfeProtocolException
    {
        // do a single inventory and print out the list
        System.out.println("Do SingleInventory");
        List<JrfeTagEvent> tagList;
       
        tagList = _ph.doSingleInventory();
       
        System.out.println("\t\t -> Found " + tagList.size() + " Tags");
        for(int i = 0; i < tagList.size(); i++)
        {
        	JrfeTagEvent t = tagList.get(i);
            System.out.println("\t\t #" + i + " " + t.toString());	
        }
    }
    
    public void test_ReadTIDUM() throws JrfeProtocolException
    {
        JrfePURprotocolHandler pur_ph = (JrfePURprotocolHandler)_ph;
        if (pur_ph == null)
        {
            System.out.println("Unable to test. Protocol implementation is not valid. Should be PUR.");
            return;
        }

        System.out.println("Trying to read tid register:");

        // do a single inventory and print out the tag list with an index to select one tag
        System.out.println("\t -> 1, Searching for tags:");
        List<JrfeTagEvent> tagList;
        tagList = _ph.doSingleInventory();
        System.out.println("\t\t Found "+ tagList.size() + " Tags");
        if (tagList.size() > 0)
        {
            for (int i = 0; i < tagList.size(); i++)
                System.out.println("\t\t\t [" + (i + 1) + "]: {" + JrfeHelper.toHexString(tagList.get(i).tagId) + "}");
        }
        else
        {
            System.out.println("\t\t Found 0 Tags, stopping the test...");
            return;
        }
        System.out.println("");

        // read the selected index
        System.out.print("\t -> 2, Select a tag by index: > ");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String sIndex = "";
        try {
        	sIndex = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
        int index = Integer.parseInt(sIndex) - 1;
        if (index < 0 || index >= tagList.size())
        {
            System.out.println("\t\t Selected unkown tag, stopping the test...");
            return;
        }
        System.out.println("");

        ////////////////////////////////////////////////// READ
        // try to read the first 4 bytes of the TID membank of the selected tag
        byte[] passwd = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
        byte[] data;
        
        System.out.println("\t -> 3, Trying to read TID register:");

        data = _ph.readFromTag(tagList.get(index).tagId, (byte)0x02, (short)0, passwd, (byte)4);

        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < data.length; i++)
        {
            System.out.print(JrfeHelper.toHexString(data[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");

        ////////////////////////////////////////////////// READ
        // try to read the whole user memory bank
        System.out.println("\t -> 4, Trying to read UM register:");
        
        data = _ph.readFromTag(tagList.get(index).tagId, (byte)0x03, (short)0, passwd, (byte)0);

        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < data.length; i++)
        {
            System.out.print(JrfeHelper.toHexString(data[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");
    }

    public void test_ReadWriteUser() throws JrfeProtocolException
    {
        JrfePURprotocolHandler pur_ph = (JrfePURprotocolHandler)_ph;
        if (pur_ph == null)
        {
            System.out.println("Unable to test. Protocol implementation is not valid. Should be PUR.");
            return;
        }

        System.out.println("Trying to read and write user register:");

        // do a single inventory and print out the tag list with an index to select one tag
        System.out.println("\t -> 1, Searching for tags:");
        List<JrfeTagEvent> tagList;
        tagList = _ph.doSingleInventory();
        System.out.println("\t\t Found " + tagList.size() + " Tags");
        if (tagList.size() > 0)
        {
            for (int i = 0; i < tagList.size(); i++)
                System.out.println("\t\t\t [" + (i + 1) + "]: {" + JrfeHelper.toHexString(tagList.get(i).tagId) + "}");
        }
        else
        {
            System.out.println("\t\t Found 0 Tags, stopping the test...");
            return;
        }
        System.out.println("");

        // read the selected index
        System.out.print("\t -> 2, Select a tag by index: > ");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String sIndex = "";
        try {
        	sIndex = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
        int index = Integer.parseInt(sIndex) - 1;
        if (index < 0 || index >= tagList.size())
        {
            System.out.println("\t\t Selected unkown tag, stopping the test...");
            return;
        }
        System.out.println("");

        byte memBank = 0x03;
        byte[] passwd = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
        byte[] userMemBefore;
        byte[] userMemAfter;
        byte[] old4Bytes = new byte[4];
        byte[] new4Bytes = new byte[] { 0x11, 0x22, 0x33, 0x44 };

        // try to read the whole user memory bank
        System.out.println("\t -> 3, Trying to Read all bytes from user mem");
        userMemBefore = _ph.readFromTag(tagList.get(index).tagId, memBank, (short)0, passwd, (byte)0);

        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < userMemBefore.length; i++)
        {
        	System.out.print(JrfeHelper.toHexString(userMemBefore[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");

        // save the old first 4 bytes of the user memory
        old4Bytes[0] = userMemBefore[0];
        old4Bytes[1] = userMemBefore[1];
        old4Bytes[2] = userMemBefore[2];
        old4Bytes[3] = userMemBefore[3];

        // try to write 4 dummy bytes to the user mem
        System.out.println("\t -> 4, Trying to Wrtie data:");
        System.out.println("\t\t\t " + JrfeHelper.toHexString(new4Bytes));
        _ph.writeToTag(tagList.get(index).tagId, memBank, (short)0, passwd, new4Bytes);
        System.out.println("\t\t Wrtie succeeded");
        System.out.println("");

        // try to read the whole user me again
        System.out.println("\t -> 5, Trying to Read written data");
        userMemAfter = _ph.readFromTag(tagList.get(index).tagId, memBank, (short)0, passwd, (byte)0);

        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < userMemAfter.length; i++)
        {
        	System.out.print(JrfeHelper.toHexString(userMemAfter[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");

        // try to write the old bytes into the memory
        System.out.println("\t -> 6, Trying to restore the bytes");
        _ph.writeToTag(tagList.get(index).tagId, memBank, (short)0, passwd, userMemBefore);
        System.out.println("\t\t Wrtie succeeded");
        System.out.println("");

        // again read the restored bytes from the tag
        System.out.println("\t -> 7, Trying to Read written data");
        userMemAfter = _ph.readFromTag(tagList.get(index).tagId, memBank, (short)0, passwd, (byte)4);
        System.out.println("\t\t Read succeeded, read data: ");
        System.out.print("\t\t\t " + JrfeHelper.toHexString(userMemAfter));
        System.out.println("");
    }

    public void test_ReadBlockWriteUser() throws JrfeProtocolException
    {
        byte blockSize = 2;
        byte wordCount = 16;

        JrfePURprotocolHandler pur_ph = (JrfePURprotocolHandler)_ph;
        if (pur_ph == null)
        {
            System.out.println("Unable to test. Protocol implementation is not valid. Should be PUR.");
            return;
        }

        System.out.println("Trying to read and write user register:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        System.out.print("What wordCount should be used? [1-110] (16)> ");
        String wC = "";
        try {
        	wC = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
        if(wC.isEmpty())
        	wordCount = 16;
        else
        	wordCount = (byte)Integer.parseInt(wC);


        System.out.print("What blockSize should be used? [1-4] (2)> ");
        String bS = "";
        try {
        	bS = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
        if(bS.isEmpty())
        	blockSize = 2;
        else
        	blockSize = (byte)Integer.parseInt(bS);

        int size = wordCount * 2;


        // do a single inventory and print out the tag list with an index to select one tag
        System.out.println("\t -> 1, Searching for tags:");
        List<JrfeTagEvent> tagList;
        tagList = _ph.doSingleInventory();
        System.out.println("\t\t Found " +  tagList.size() + " Tags");
        if (tagList.size() > 0)
        {
            for (int i = 0; i < tagList.size(); i++)
            	System.out.println("\t\t\t [" + (i + 1) + "]: {" + JrfeHelper.toHexString(tagList.get(i).tagId) + "}");
        }
        else
        {
            System.out.println("\t\t Found 0 Tags, stopping the test...");
            return;
        }
        System.out.println("");

        // read the selected index
        System.out.print("\t -> 2, Select a tag by index: > ");
        String sIndex = "";
        try {
        	sIndex = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
        int index = Integer.parseInt(sIndex) - 1;
        if (index < 0 || index >= tagList.size())
        {
            System.out.println("\t\t Selected unkown tag, stopping the test...");
            return;
        }
        System.out.println("");

        byte memBank = 0x03;
        byte[] passwd = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
        byte[] userMemBefore;
        byte[] userMemAfter;
        byte[] oldBytes = new byte[size];
        byte[] newBytes = new byte[size];

        for (int i = 0; i < size; i++)
        {
            newBytes[i] = (byte)(i + 1 + ((i + 1) * 16));
        }

        ////////////////////////////////////////////////// READ
        // try to read the whole user memory bank
        System.out.println("\t -> 3, Trying to Read all bytes from user mem");
        userMemBefore = _ph.readFromTag(tagList.get(index).tagId, memBank, (short)0, passwd, (byte)0);

        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < userMemBefore.length; i++)
        {
        	System.out.print(JrfeHelper.toHexString(userMemBefore[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");

        // save the old first 4 bytes of the user memory
        for (int i = 0; i < size; i++)
        {
            oldBytes[i] = userMemBefore[i];
        }


        ////////////////////////////////////////////////// BLOCKWRITE
        // try to write 4 dummy bytes to the user mem
        System.out.println("\t -> 4, Trying to Wrtie data:");
        System.out.println("\t\t\t " + JrfeHelper.toHexString(newBytes));
        pur_ph.blockWriteToTag(tagList.get(index).tagId, memBank, (short)0, passwd, blockSize, newBytes);
        System.out.println("\t\t Wrtie succeeded");
        System.out.println("");


        ////////////////////////////////////////////////// READ
        // try to read the whole user me again
        System.out.println("\t -> 5, Trying to Read written data");
        userMemAfter = _ph.readFromTag(tagList.get(index).tagId, memBank, (short)0, passwd, (byte)0);

        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < userMemAfter.length; i++)
        {
        	System.out.print(JrfeHelper.toHexString(userMemAfter[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");


        ////////////////////////////////////////////////// BLOCKWRITE
        // try to write the old bytes into the memory
        System.out.println("\t -> 6, Trying to restore the bytes");
        pur_ph.blockWriteToTag(tagList.get(index).tagId, memBank, (short)0, passwd, blockSize, oldBytes);
        System.out.println("\t\t Wrtie succeeded");
        System.out.println("");


        ////////////////////////////////////////////////// READ
        // again read the restored bytes from the tag
        System.out.println("\t -> 7, Trying to Read written data");
        userMemAfter = _ph.readFromTag(tagList.get(index).tagId, memBank, (short)0, passwd, (byte)0);
        System.out.println("\t\t Read succeeded, read data:");
        System.out.print("\t\t\t ");
        for (int i = 0; i < userMemAfter.length; i++)
        {
        	System.out.print(JrfeHelper.toHexString(userMemAfter[i]) + "-");
            if (((i + 1) % 8) == 0)
                System.out.print("\n\t\t\t ");
        }
        System.out.print("\n");
        System.out.println("");
    }


    public void test_AN001_ReadTIDFirstTag_Slow() throws JrfeProtocolException
    {

        JrfePURprotocolHandler pur_ph = (JrfePURprotocolHandler)_ph;
        if (pur_ph == null)
        {
            System.out.println("Unable to test. Protocol implementation is not valid. Should be PUR.");
            return;
        }

        System.out.println("Trying to read tid register:");

        _storedTagIdSema = new Semaphore(0);
        _storedTagId = null;
        _storeCyclicInventoryEvent = true;
        
        System.out.println("\t -> 1, Searching for tags:");

        _tagReadCount = 0;
        pur_ph.setCyclicInventory(true);

        try {
			_storedTagIdSema.tryAcquire(5, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
	        _storeCyclicInventoryEvent = false;
			return;
		}

        pur_ph.setCyclicInventory(false);

        if (_storedTagId == null)
        {
            System.out.println("\t\t Did not find any tag...");
            _storeCyclicInventoryEvent = false;
            return;
        }

        System.out.println("\t\t Found Tag " + JrfeHelper.toHexString(_storedTagId));


        // try to read the first 4 bytes of the TID membank of the detected tag
        System.out.println("\t -> 2, Trying to read first 4 bytes of TID register:");
        byte[] passwd = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
        byte[] data;

        long startTime = System.currentTimeMillis();
        data = pur_ph.readFromTag(_storedTagId, (byte)0x02, (short)0, passwd, (byte)2);
        long stopTime = System.currentTimeMillis();

        System.out.println("\t\t Read succeeded, read data: " + JrfeHelper.toHexString(data));
        System.out.println("");
        System.out.println("Read Result " + (stopTime - startTime) + "ms after the tag was detected");
        System.out.println("");

        _storeCyclicInventoryEvent = false;
    }

    public void test_AN001_ReadTIDFirstTag_Fast() throws JrfeProtocolException
    {
        JrfePURprotocolHandler pur_ph = (JrfePURprotocolHandler)_ph;
        if (pur_ph == null)
        {
            System.out.println("Unable to test. Protocol implementation is not valid. Should be PUR.");
            return;
        }

        System.out.println("Trying to read tid register:");

        _storedTagIdSema = new Semaphore(0);
        _storedTagId = null;
        _storeCyclicInventoryEvent = true;
        
        System.out.println("\t -> 1, Searching for tags:");

        _tagReadCount = 0;
        pur_ph.setCyclicInventory(true);

        try {
			_storedTagIdSema.tryAcquire(5, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
            pur_ph.setCyclicInventory(false);
            _storeCyclicInventoryEvent = true;
            pur_ph.setBlockCyclicInventoryInterrupts(false);
            return;
		}
        pur_ph.setBlockCyclicInventoryInterrupts(true);

        if (_storedTagId == null)
        {
            System.out.println("\t\t Did not find any tag...");
            pur_ph.setCyclicInventory(false);
            _storeCyclicInventoryEvent = true;
            pur_ph.setBlockCyclicInventoryInterrupts(false);
            return;
        }

        System.out.println("\t\t Found Tag " + JrfeHelper.toHexString(_storedTagId));

        // try to read the first 4 bytes of the TID membank of the detected tag
        System.out.println("\t -> 2, Trying to read first 4 bytes of TID register:");
        byte[] passwd = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
        byte[] data;
        
        long startTime = System.currentTimeMillis();
        data = pur_ph.readFromTag(_storedTagId, (byte)0x02, (short)0, passwd, (byte)2);
        long stopTime = System.currentTimeMillis();

        System.out.println("\t\t Read succeeded, read data: " + JrfeHelper.toHexString(data));
        System.out.println("");
        System.out.println("Read Result " + (stopTime - startTime) + "ms after the tag was detected");
        System.out.println("");

        pur_ph.setCyclicInventory(false);
        _storeCyclicInventoryEvent = true;
        pur_ph.setBlockCyclicInventoryInterrupts(false);
    }

    public void test_AN001_ReadTIDFirstTag_Handle() throws JrfeProtocolException
    {
        JrfePURprotocolHandler pur_ph = (JrfePURprotocolHandler)_ph;
        if (pur_ph == null)
        {
            System.out.println("Unable to test. Protocol implementation is not valid. Should be PUR.");
            return;
        }

        System.out.println("Trying to read tid register from first tag:");

        pur_ph.setParam((short) 0x24, new byte[] { 0x01 });        //send Handle ON
        pur_ph.setParam((short) 0x00, new byte[] { 0x04 });        //inventory_secure_single_tag
        pur_ph.setParam((short) 0x03, new byte[] { 0x02 });        //sendTagIDImmediatelyAndStop

        _storedTagIdSema = new Semaphore(0);
        _storedTagEvent = null;
        _storeCyclicInventoryEvent = true;

        System.out.println("\t -> 1, Searching for tags and reading from first");

        pur_ph.setAntennaPower(true);

        _tagReadCount = 0;
        pur_ph.setCyclicInventory(true);

        try {
			_storedTagIdSema.tryAcquire(5, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
            pur_ph.setCyclicInventory(false);
	        pur_ph.setAntennaPower(false);
            pur_ph.setParam((short) 0x24, new byte[] { 0x00 });  //send Handle OFF
            pur_ph.setParam((short) 0x00, new byte[] { 0x02 });  //inventory_secure_multi_tag
            pur_ph.setParam((short) 0x03, new byte[] { 0x00 });  //sendTagIDImmediately

            _storeCyclicInventoryEvent = true;
            pur_ph.setBlockCyclicInventoryInterrupts(false);
            return;
		}
        pur_ph.setBlockCyclicInventoryInterrupts(true);

        if (_storedTagEvent == null || _storedTagEvent.hasHandle == false)
        {
            System.out.println("\t\t Did not find any tag...");
            pur_ph.setCyclicInventory(false);
            pur_ph.setAntennaPower(false);
            pur_ph.setParam((short) 0x24, new byte[] { 0x00 });  //send Handle OFF
            pur_ph.setParam((short) 0x00, new byte[] { 0x02 });  //inventory_secure_multi_tag
            pur_ph.setParam((short) 0x03, new byte[] { 0x00 });  //sendTagIDImmediately

            _storeCyclicInventoryEvent = true;
            pur_ph.setBlockCyclicInventoryInterrupts(false);
            return;
        }

        System.out.println("\t\t Found Tag " + JrfeHelper.toHexString(_storedTagEvent.tagId));


        // try to read the first 4 bytes of the TID membank of the detected tag
        System.out.println("\t -> 2, Trying to read first 4 bytes of TID register:");
        byte[] passwd = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };
        byte[] data;

        long startTime = System.currentTimeMillis();
        data = pur_ph.readFromHandle(_storedTagEvent.handle, (byte)0x02, (short)0, passwd, (byte)2);
        long stopTime = System.currentTimeMillis();

        System.out.println("\t\t Read succeeded, read data: " + JrfeHelper.toHexString(data));
        System.out.println("");
        System.out.println("Read Result " + (stopTime - startTime) + "ms after the tag was detected");
        System.out.println("");

        
        pur_ph.setCyclicInventory(false);
        pur_ph.setAntennaPower(false);
        pur_ph.setAntennaPower(false);
        pur_ph.setParam((short) 0x24, new byte[] { 0x00 });  //send Handle OFF
        pur_ph.setParam((short) 0x00, new byte[] { 0x02 });  //inventory_secure_multi_tag
        pur_ph.setParam((short) 0x03, new byte[] { 0x00 });  //sendTagIDImmediately

        _storeCyclicInventoryEvent = true;
        pur_ph.setBlockCyclicInventoryInterrupts(false);
    }
    
    /********************************************************************************************* Event Handler */

    /** Holds the time stamp of the last heart beat */
    protected long _lastHeartbeat = System.nanoTime();
    /** Holds the count of cyclic tag reads */
    protected long _tagReadCount = 0;
    /** Semaphore to notify about stored tag read */
    protected Semaphore _storedTagIdSema = new Semaphore(0);
    /** Stored tag id */
    protected byte[] _storedTagId = null;
    /** Stored tag event */
    protected JrfeTagEvent _storedTagEvent = null;
    /** Holds if the tag event should be stored, otherwise wil be printed */
    protected boolean _storeCyclicInventoryEvent = false;
        
    /** 
     * Event handler for the heartbeat event
     * @param data 		Data
     */
    public void onHeartBeat(byte[] data)
    {
    	long elapsedTime = System.currentTimeMillis() - _lastHeartbeat;
        System.out.println("\t\t Heartbeat after " + (elapsedTime) + " ms / data: " + JrfeHelper.toHexString(data));
        _lastHeartbeat = System.currentTimeMillis();
    }

    /**
     * Event handler for the state changed event
     * @param newState	New state
     */
    public void onStateChanged(int newState)
    {
        System.out.println("\t\t =============== State changed to: " + eRFE_CURRENT_READER_STATE.toString(newState) + " ===============");
    }

    /**
     * Event handler for the state changed event
     * @param gpioValues	Changed gpio values
     */
    public void onGpioValuesChanged(Integer gpioValues)
    {
        System.out.println("\t\t GPIO Values Changed: = " + JrfeHelper.toHexString(gpioValues));
    }

    /**
     * Event handler for cyclic inventory event.
     * Prints the tag event.
     * @param tagEvent		Tag event
     */
    public void onCyclicInventoryPrint(JrfeTagEvent tagEvent)
    {
    	System.out.print("\t\t" + (++_tagReadCount) + "  " + JrfeHelper.toHexString(tagEvent.tagId) + " ");

        if (tagEvent.hasMemory)
        {
        	System.out.print("MEM " + JrfeHelper.toHexString(tagEvent.memData) + " @" + tagEvent.memBank + "." + tagEvent.memAddr + " ");
        }

        if (tagEvent.hasApplicationInfo)
        {
        	System.out.print("APP " + JrfeHelper.toHexString(tagEvent.applicationInfo) + " ");
        }

        System.out.println("");

        if (tagEvent.hasRSSI)
        {
            if (JrfePURprotocolHandler.class.getName() == _ph.getClass().getName())
            {
            	System.out.print("\t\t\t\tQ: ");
                for (int i = 0; i < tagEvent.rssi[0]; i++)
                	System.out.print("#");
                System.out.print("\n");

                System.out.print("\t\t\t\tI: ");
                for (int i = 0; i < tagEvent.rssi[1]; i++)
                	System.out.print("#");
                System.out.print("\n");
            }

            if (JrfeAURprotocolHandler.class.getName() == _ph.getClass().getName())
            {
                short rssi = JrfeHelper.toShort(tagEvent.rssi,  0,  2);
                System.out.print("\t\t\t\tRSSI: " + rssi + "\n");
            }
        }
    }

    /**
     * Event handler for cyclic inventory event.
     * Stores the tag event.
     * @param tagEvent		
     */
    public void onCyclicInventoryStoreTagId(JrfeTagEvent tagEvent)
    {
        if (_storedTagId == null)
        {
            _storedTagId = new byte[tagEvent.tagId.length];
            System.arraycopy(tagEvent.tagId, 0, _storedTagId, 0, tagEvent.tagId.length);
        }

        if (_storedTagEvent == null)
        {
            _storedTagEvent = new JrfeTagEvent(tagEvent);
        }

        if(_storedTagIdSema != null)
        	_storedTagIdSema.release();
    }
    
    
    protected JrfeProtocolHandler.Listener  m_listner = new JrfeProtocolHandler.Listener() {
		
		@Override
		public void _onStatusRegisterChanged(Long statusRegister) {
			return;
		}
		
		@Override
		public void _onStateChanged(int newState) {
			onStateChanged(newState);
		}
		
		@Override
		public void _onNotification(byte[] payload) {
			return;
		}
		
		@Override
		public void _onHeartBeat(byte[] data) {
			onHeartBeat(data);
		}
		
		@Override
		public void _onGpioValuesChanged(Integer gpioValues) {
			onGpioValuesChanged(gpioValues);
		}
		
		@Override
		public void _onCyclicInventory(JrfeTagEvent tagEvent) {
			if(!_storeCyclicInventoryEvent)
				onCyclicInventoryPrint(tagEvent);
			else
				onCyclicInventoryStoreTagId(tagEvent);
		}
		
		@Override
		public void _onApplicationEvent(byte[] payload) {
			return;
		}
	};


    /********************************************************************************************* Helper */

	/**
	 * Creates an instance with the given device and type
	 * @param device	The communication device to the reader
	 * @return			Returns the new created instance of the protocol handler
	 */
    private JrfeProtocolHandler createInstance(IProtocolDeviceInterface device)
    {
    	JrfeProtocolHandler p = createInstanceExtended(device, type);

        if (p != null)
            return p;

        return new JrfeProtocolHandler(device);
    }

    /**
     * Prints the test table to the console
     */
    private void printTestTable()
    {
        System.out.println("+------------------------ MENU ----------------------------+");
        System.out.println("|    q = Quit                                              |");
        System.out.println("|    1 = Query Reader Information                          |");
        System.out.println("|    2 = Test Attenuation Settings                         |");
        System.out.println("|    3 = Test Frequency Settings                           |");
        System.out.println("|    4 = Test Sensitivity Settings                         |");
        System.out.println("|    5 = Test Heartbeat                                    |");
//        System.out.println("|    6 = Test GPIO - Output                                |");
//        System.out.println("|    7 = Test GPIO - Input                                 |");
        System.out.println("|    8 = Start Inventory                                   |");
        System.out.println("|    9 = Do SinglInventory                                 |");
        System.out.println("+-PUR-----------------------------------------------------+");
        System.out.println("|   21 = Start RSSI Inventory                              |");
//        System.out.println("|   22 = Start PostDetect READ Inventory                   |");
        System.out.println("|   31 = Try to Read TID & UM                              |");
        System.out.println("|   32 = Try to Read/Write User Mem                        |");
        System.out.println("|   33 = Try to Read/Block-Write User Mem                  |");
        System.out.println("+-AN-PUR---------------------------------------------------+");
        System.out.println("|   41 = AN001 - PUR - Test Read TID of First Tag - Slow   |");
        System.out.println("|   42 = AN001 - PUR - Test Read TID of First Tag - Fast   |");
        System.out.println("|   43 = AN001 - PUR - Test Read TID of First Tag - Handle |");
//        System.out.println("+-CUSTOM-TAG-----------------------------------------------+");
//        System.out.println("|   51 = SL900A - Get Temperature                          |");
//        System.out.println("|   52 = 4325 - Get Temperature                            |");
        System.out.println("+----------------------------------------------------------+");
        printTestTableExtended();
    }

    /**
     * Calls the given test routine
     * @param id	ID of the test routine
     * @return		Returns if test routine is implemented.
     * @throws JrfeProtocolException
     */
    private boolean callTestRoutine(int id) throws JrfeProtocolException
    {
        if (id == 1)
            test_ReaderInfo();
        else if (id == 2)
            test_Attenuation();
        else if (id == 3)
            test_Frequency();
        else if (id == 4)
            test_Sensitivity();
        else if (id == 5)
            test_Heartbeat();
//        else if (id == 6)
//            test_GPIO_Output();
//        else if (id == 7)
//            test_GPIO_Input();
        else if (id == 8)
            test_Inventory();
        else if (id == 9)
            test_SingleInventory();

        else if (id == 21)
            test_InventoryRSSI();
//        else if (id == 22)
//            test_InventoryPostDetectREAD();

        else if (id == 31)
            test_ReadTIDUM();
        else if (id == 32)
            test_ReadWriteUser();
        else if (id == 33)
            test_ReadBlockWriteUser();

        else if (id == 41)
            test_AN001_ReadTIDFirstTag_Slow();
        else if (id == 42)
            test_AN001_ReadTIDFirstTag_Fast();
        else if (id == 43)
            test_AN001_ReadTIDFirstTag_Handle();

//        else if (id == 51)
//            test_SL900A_GetTemperature();
//        else if (id == 52)
//            test_4325_GetTemperature();

        else
            return callTestRoutinesExtended(id);

        return true;
    }
    
    

    
    /********************************************************************************************* Overrideables */

    /**
     * Override this function to create a communication device without querying in the console.
     * @return		Returns the communication device.
     */
    public IProtocolDeviceInterface getTestDevice()
    {
        return null;
    }

    /**
     * Override this function to create an own instance of the protocol handler class.
     * @param device	The communication device to the reader
     * @return			Returns the new instance, null if none was created.
     */
    protected JrfeProtocolHandler createInstanceExtended(IProtocolDeviceInterface device)
    {
        return null;
    }

    /**
     * Override this function to add an own function to the test table output.
     */
    protected void printTestTableExtended()
    {
    }

    /**
     * Override this function so your own function can be called
     * @param id		ID of your own functions
     * @return			Returns if a function with this id is implemented.
     */
    protected boolean callTestRoutinesExtended(int id)
    {
        return false;
    }

}
